﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Google.Api.Maps.Service.StaticMaps;

namespace NextViewer
{
    class Shell
    {

        public Shell()
        {
            mCom.map = new StaticMap();
            mCom.map.Size = "640x640";
            //mCom.map.Zoom = "14";

            //mCom.mPoint = new Points();
            mCmds = new List<string>();
            mCom = new Commands(this);
        }

        //testsets
        /*
           //사람 리스트
           //mCom.user = "359964040471280";      // 재영이형 어머님
           //mCom.user = "A1000019A0ACBA";       //에러
           //mCom.user = "99000072395041";       //에러 2
           //mCom.user = "357470045716036";      //분당 ....?
           //mCom.user = "353930052385946";
           //mCom.user = "359506045228940";     // 재영형
           //mCom.user = "358414046179405";    // 초롱이
           //mCom.user = "A000002AEDA8AB";
           //mCom.user = "356951042039044";
            */
        private void getTestset()
        {
            //override testset1
            // 초롱이 월요일 출근 - 병원경유
            mCom.user = "358414046179405";
            mCom.dtStart = DateTime.Parse("2012-10-29 08:30:00");
            mCom.dtEnd = DateTime.Parse("2012-10-29 12:00:07");

            //override testset2
            //초롱이 수면
            //mCom.user = "358414046179405";
            //mCom.dtStart = DateTime.Parse("2012-10-23 23:30:00");
            //mCom.dtEnd = DateTime.Parse("2012-10-24 06:00:07");
            
            //override testset3
            //초롱이 화~금요일 출근
            //mCom.user = "358414046179405";
            //mCom.dtStart = DateTime.Parse("2012-10-23 08:30:00");
            //mCom.dtEnd = DateTime.Parse("2012-10-23 12:00:07");

            //testset4
            //재영형 출근
            //mCom.user = "359506045228940";
            //mCom.dtStart = DateTime.Parse("2012-10-22 08:30:00");
            //mCom.dtEnd = DateTime.Parse("2012-10-22 11:00:07");

            //testset5
            
            mCom.user = "359506045228940";
            mCom.dtStart = DateTime.Parse("2012-10-29 20:30:00");
            mCom.dtEnd = DateTime.Parse("2012-10-29 22:30:07");
            
            //tst 6
            mCom.user = "359506045228940";
            mCom.dtStart = DateTime.Parse("2012-10-13 16:15:00");
            mCom.dtEnd = DateTime.Parse("2012-10-13 17:40:07");
            /*
            //tst 7
            mCom.user = "356951042039044";
            mCom.dtStart = DateTime.Parse("2012-10-30 11:07:00");
            mCom.dtEnd = DateTime.Parse("2012-10-30 11:24:07");
            
            //8 : fake gps
            
            mCom.user = "358414046179405";
            mCom.dtStart = DateTime.Parse("2012-12-1 10:57:00");
            mCom.dtEnd = DateTime.Parse("2012-12-1 12:20:07");
            
            mCom.user = "358414046179405";
            mCom.dtStart = DateTime.Parse("2012-11-30 22:49:00");
            mCom.dtEnd = DateTime.Parse("2012-11-30 23:20:00");
            
            // 매우 미친듯이 돌아가는 튄(...)
            
            mCom.user = "358414046179405";
            mCom.dtStart = DateTime.Parse("2012-12-1 19:31:00");
            mCom.dtEnd = DateTime.Parse("2012-12-1 19:57:07");
            */
        }

        public Shell(NextViewer nv)
        {
            mCmds = new List<string>();
            mCom = new Commands(this);
            mCom.Terminal = nv;

            mCom.map = new StaticMap();
            mCom.map.Size = "640x640";
            //mCom.map.Zoom = "14";
           getTestset();
        }

        public void Parser(string cmd, NextViewer nv)
        {
            mCmds.Add(cmd); 
            switch (cmd[0])
            {
                case 'A':
                    UpdatePic_app(cmd, nv);
                    break;
                case 'M':
                    mCom.UpdatePic(cmd);
                    break;
                case 'D':
                    ShowDB(cmd,nv);
                    break;
                case 'U':
                    mCom.Enviroment(cmd);
                    break;
                default:
                    return;
            }
        }

        private void ShowDB(string cmd,NextViewer nv)
        {
            string query = cmd.Substring(2);
            string[] res = DBMS.GetDBMS().GetResult(query +" "+ addon);

            nv.updateConsole(res);
        }

        // mPoint에 들어있는 점들의 위치/보정위치/속도/가속도/방향을 출력한다.
        private void printInfo(NextViewer nv)
        {
            string str;
            for (int i = 1; i < mCom.mPoint.mPt.Count; i++)
            {
                str = "\t(" + mCom.mPoint.mPt[i - 1].Lat.ToString("0.0000")
                       + ", " + mCom.mPoint.mPt[i - 1].Lon.ToString("0.0000") + ") ("
                       + mCom.mPoint.mPt[i].Lat.ToString("0.0000") + ", "
                       + mCom.mPoint.mPt[i].Lon.ToString("0.0000") + ")\t"
                       + "dt(sec): "
                       + mCom.mPoint.mPt[i].tm_span.TotalSeconds
                       + "\t dist(m): "
                       + getDistance(mCom.mPoint.mPt[i - 1], mCom.mPoint.mPt[i]).ToString("0.0000")
                       + " \tspd: "
                       + "\t(" + mCom.mPoint.mPt[i].velocity.ToString("0.000") + ") "
                       + "\t(m/s)\tAcc:" + mCom.mPoint.mPt[i].accel
                       + "(m/s2)\t " + mCom.mPoint.mPt[i].acc_angle
                       + "º   " + mCom.mPoint.mPt[i].tm_slope
                       + "\tApen :" + mCom.mPoint.mPt[i].pen_a
                       + "\tBpen :" + mCom.mPoint.mPt[i].pen_b
                       + "\tCpen :" + mCom.mPoint.mPt[i].pen_c
                       + "\tGPS pos : (" + mCom.mPoint.mPt[i].gpsLat + " , " + mCom.mPoint.mPt[i].gpsLon + " )";
                nv.SetText(str);
            }
        }

        //unused
        private void filtering(NextViewer nv)
        {
            string str;
            bool removed=false;
            for (int i = 1; i < mCom.mPoint.mPt.Count; i++)
            {
                
                if (mCom.mPoint.mPt[i].velocity > 30.0)
                {
                    str = " \tspd: " + "\t(" + mCom.mPoint.mPt[i].velocity.ToString("0.000") + ")  Removed";
                    mCom.mPoint.mPt.RemoveAt(i);
                    i--;
                    nv.SetText(str);
                    removed = true;
                }
                 
                /*
                if (mCom.mPoint.mPt[i].dist_prev > 5000.0)
                {
                    str = " \tdist: " + "\t(" + mCom.mPoint.mPt[i].dist_prev.ToString("0.000") + ")  Removed";
                    mCom.mPoint.mPt.RemoveAt(i);
                    i--;
                    nv.SetText(str);
                    //removed = true;
                }
                 */
            }
            if (removed)
            {
                nv.SetText("-------");
                getFitness(true);
                filtering(nv);
            }
        }
        
        private void throwing(NextViewer nv)
        {
            string str;
            int avcount=0;
            double averspeed = 0.0;
            double speedsum=0.0;
            double tempd=0.0;
            double exaver=0.0;
            int mm=mCom.mPoint.mPt.Count/ 20;
            double[] maxvalue = new double[mm];
            Pot prevpoint = null;
            for (int i = 0; i < mm; i++)
            {
                maxvalue[i] = -1.0;
            }
            for (int i = 1; i < mCom.mPoint.mPt.Count; i++)
            {
                tempd=mCom.mPoint.mPt[i].velocity;
                for (int j = 0; j < mm; j++)
                {
                    if (maxvalue[j] < tempd)
                    {
                        for (int k = mm-2; k >=j ; k--)
                        {
                            maxvalue[k + 1] = maxvalue[k];
                        }
                        maxvalue[j] = tempd;
                        break;
                    }
                }
                speedsum = speedsum + tempd;
                avcount++;
            }
            exaver = speedsum;
            for (int i = 0; i < mm; i++)
            {
                exaver = exaver - maxvalue[i];
            }
            exaver = exaver / (avcount - mm);
            
            for (int i = 1; i < mCom.mPoint.mPt.Count; i++)
            {
                tempd = mCom.mPoint.mPt[i].velocity;
                if (tempd > 30.0 && tempd > exaver * 2.0)
                {
                    mCom.mPoint.mPt[i].thro = true;
                }
            }
            bool sw = false;
            int cnt = 0;
            for (int i = 1; i < mCom.mPoint.mPt.Count; i++)
            {
                if (mCom.mPoint.mPt[i].thro)
                {
                    sw = !sw;
                }
                mCom.mPoint.mPt[i].thro = sw;
                if (sw)
                { 
                    cnt++;
                    if (cnt > 3)
                    {
                        sw = !sw;
                        cnt = 0;
                    }
                }
                else
                {  cnt=0;}
            }
            for (int i = 0; i < mCom.mPoint.mPt.Count; i++)
            {
                if (mCom.mPoint.mPt[i].thro)
                {
                    mCom.mPoint.mPt[i].appLat = prevpoint.Lat;
                    mCom.mPoint.mPt[i].appLon = prevpoint.Lon;
                }
                else
                {
                    prevpoint = mCom.mPoint.mPt[i];
                }
            }
        }

        /*
        public void setPoints(DateTime qt,List<Pot> plist,Pot ret,NextViewer nv)
        {
            string str = "";

            return;
        }
        */

        private void vs_gps(NextViewer nv)
        {
            int i = 0;
            string str="";
            Stat<double> diststat = new Stat<double>(mCom.mPoint.mPt.Count);
            Stat<double> diststat2 = new Stat<double>(mCom.mPoint.mPt.Count);
            double aver;
            double stdev;
            double max;
            double min;
            int gpscount = 0;
            int gpscount2 = 0;
            for (i = 0; i < mCom.mPoint.mPt.Count; i++)
            {
                //재영이형이 뭔가 줌. 
                double thankyou=0.0;
                double thankyou2 = 0.0;
                thankyou = Util.GetGap(mCom.mPoint.mPt[i].Time, mCom.mPoint.mPt[i].appLat, mCom.mPoint.mPt[i].appLon);
                thankyou2 = Util.GetGap(mCom.mPoint.mPt[i].Time, mCom.mPoint.mPt[i].Lat, mCom.mPoint.mPt[i].Lon);
                if (thankyou > 0.0)
                {
                    diststat.Add(thankyou);
                    gpscount++;
                }
                if (thankyou2 > 0.0)
                {
                    diststat2.Add(thankyou2);
                    gpscount2++;
                }
            }
            if (gpscount > 0)
            {
                aver = diststat.GetAvr();
                stdev = diststat.GetStdev();
                max = diststat.GetMax();
                min = diststat.GetMin();
                str = "보정 데이터 \taverage : " + aver.ToString("0.0000") + "km\tstdev : " + stdev.ToString("0.0000")
                    + "km\tmax : " + max.ToString("0.0000") + "km\tmin : " + min.ToString("0.0000");
            }
            else
            {
                str = "해당 gps데이터가 하나도 없습니다.";
            }
            nv.SetText(str);
            if (gpscount2 > 0)
            {
                aver = diststat2.GetAvr();
                stdev = diststat2.GetStdev();
                max = diststat2.GetMax();
                min = diststat2.GetMin();
                str = "원래 데이터\taverage : " + aver.ToString("0.0000") + "km\tstdev : " + stdev.ToString("0.0000")
                    + "km\tmax : " + max.ToString("0.0000") + "km\tmin : " + min.ToString("0.0000");
            }
            else
            {
                str = "해당 gps데이터가 하나도 없습니다.";
            }
            nv.SetText(str);
        }

        private int findfirstindex(List<DateTime> dtl, DateTime dt)
        {
            int ret = 0;
            TimeSpan span;
            for (ret = 0; ret < dtl.Count; ret++)
            {
                span = dtl[ret].Subtract(dt);
                if (span.TotalSeconds < 60 && span.TotalSeconds > -60)
                {
                    return ret;
                }
            }
            return -1;
        }

        private void vs_fake_gps(NextViewer nv)
        {
            List<double[]> list = new List<double[]>();
            List<DateTime> list2 = new List<DateTime>();
            Util.GetFakeGPStable(list, list2);
            for (int i = 0; i < list.Count; i++)
            {
                nv.SetText("\t" + list[i][0] + "\t" + list[i][1] + "\t" + list[i][2] + "\t" + list[i][3] + "\t" + list2[i].ToString());
            }

            Stat<double> diststat = new Stat<double>(mCom.mPoint.mPt.Count);
            Stat<double> diststat2 = new Stat<double>(mCom.mPoint.mPt.Count);
            int gpscount = 0;
            int firstindex = 0;
            firstindex = findfirstindex(list2,mCom.mPoint.mPt[0].Time);
            if (firstindex == -1)
            {
                nv.SetText("FakeGPS 값이 존재하지 않습니다");
                return;
            }
            int gpsindex = firstindex;
            double thankyou,thankyou2;
            for (int i = 0; i < mCom.mPoint.mPt.Count; i++)
            {
                Pot pt = mCom.mPoint.mPt[i];
                while (list2[gpsindex] < pt.Time)
                {
                    gpsindex++;
                    if (gpsindex == list2.Count)
                        break;
                }
                if (gpsindex == list2.Count)
                    break;
                thankyou = getDistance(list[gpsindex][0], pt.appLat, list[gpsindex][1], pt.appLon);
                thankyou2 = getDistance(list[gpsindex][0], pt.Lat, list[gpsindex][1], pt.Lon);
                diststat.Add(thankyou);
                diststat2.Add(thankyou2);
                gpscount++;
            }
            if (gpscount > 0)
            {
                double aver = diststat.GetAvr();
                double stdev = diststat.GetStdev();
                double max = diststat.GetMax();
                double min = diststat.GetMin();
                string str = "보정 데이터 \taverage : " + aver.ToString("0.0000") + "m\tstdev : " + stdev.ToString("0.0000")
                    + "m\tmax : " + max.ToString("0.0000") + "m\tmin : " + min.ToString("0.0000");

                nv.SetText(str);

                aver = diststat2.GetAvr();
                stdev = diststat2.GetStdev();
                max = diststat2.GetMax();
                min = diststat2.GetMin();
                str = "원래 데이터\taverage : " + aver.ToString("0.0000") + "m\tstdev : " + stdev.ToString("0.0000")
                    + "m\tmax : " + max.ToString("0.0000") + "m\tmin : " + min.ToString("0.0000");
                nv.SetText(str);
            }
            else
            {
                nv.SetText("FakeGPS 값이 존재하지 않습니다");
            }
        }

        //run
        private void UpdatePic_app(string input, NextViewer nv)
        {
            string cmd = input.Substring(2);
            string[] cmds = cmd.Split(' ');
            if (cmds[0] == "update")
            {
                for (int i = 1; i < cmds.Length - 1; i += 2)
                {
                    if (cmds[i] == "-u")
                    {
                        mCom.user = cmds[i + 1];
                    }
                    else if (cmds[i] == "-d")
                    {
                    }
                    else if (cmds[i] == "-ps")
                    {
                        try
                        {
                            mCom.dtStart = DateTime.Parse(cmds[i + 1] + " " + cmds[i + 2]);
                        }
                        catch (Exception )
                        {
                            nv.SetText("입력은 : yyyy-mm-dd HH:MM 형태입니다.");
                            return;
                        }
                        i++;
                    }
                    else if (cmds[i] == "-pe")
                    {
                        try
                        {
                            mCom.dtEnd = DateTime.Parse(cmds[i + 1] + " " + cmds[i + 2]);
                        }
                        catch (Exception )
                        {
                            nv.SetText("입력은 : yyyy-mm-dd HH:MM 형태입니다.");
                            return;
                        }
                        i++;
                    }
                    else if (cmds[i] == "-p")
                    {

                    }
                }

                nv.SetText(mCom.map.ToUri().ToString());
            }
            else if (cmds[0] == "run")
            {
                mCom.GetRawData();
                mCom.UpdatePointsForLocation();
                //cholonge wrote
                
                InitRawData();

                //set velocity and accel
                getFitness(true);
                //printInfo(nv);
                // start
                //filtering(nv);
                printInfo(nv);
                throwing(nv);
                approximate();
                printInfo(nv);
                MakeHttpRequest_app();  //approximated route
                vs_gps(nv);
                //vs_fake_gps(nv);
                

                nv.updatePic(mCom.map);
            }
        }


        private void InitRawData()
        {
            /*   보류
            double jangc = 6378137.0; // 장축
            double danc = 6356752.314;//단축
            double f = 1 / 298.257223563; // 편평률
            double e = Math.Sqrt(1 - (1 - f) * (1 - f));  //이심률
            */
            int len = 0;
            foreach (Pot pt in mCom.mPoint.mPt)
            {
                pt.appLon = pt.Lon;
                pt.appLat = pt.Lat;
                DBMS dbm = DBMS.GetDBMS();
                string format = "yyyy-MM-dd HH:mm:ss";
                string[] param = { pt.Time.ToString(format) };
                dbm.Request("COMPGPS", param);

                dbm.Connect();

                double[] lats = new double[8];
                double[] lons = new double[8];
                double res = -1;
                len = 0;
                while (dbm.Next())
                {
                    if (dbm.data[0].ToString() == "" || dbm.data[1].ToString() == "")
                        continue;
                    lats[len] = double.Parse(dbm.data[0].ToString());
                    lons[len] = double.Parse(dbm.data[1].ToString());
                    len++;
                }
                for (int i = 0; i < len; i++)
                {
                    double temp = getDistance(pt.Lat, lats[i], pt.Lon, lons[i]);
                    if (res < temp)
                    {
                        res = temp;
                        pt.gpsLat = lats[i];
                        pt.gpsLon = lons[i];
                    }
                }
            }
            return;
        }


        // 원래 위치와 보정된 위치와의 거리 계산
        private double getDistance(Pot p1)
        {
            double R = 6371;
            double lat1 = p1.Lat * Math.PI / 180;
            double lat2 = p1.appLat * Math.PI / 180;
            double lon1 = p1.Lon * Math.PI / 180;
            double lon2 = p1.appLon * Math.PI / 180;
            if (lat1 == lat2 && lon1 == lon2)
            {
                return 0.0;
            }

            double d = Math.Acos(Math.Sin(lat1) * Math.Sin(lat2) + Math.Cos(lat1) * Math.Cos(lat2) * Math.Cos(lon2 - lon1)) * R;

            return d * 1000;
        }

        // 두 보정된 위치간의 거리 계산(점)
        private double getDistance(Pot p1, Pot p2)
        {
            double R = 6371;
            double lat1 = p1.appLat * Math.PI / 180;
            double lat2 = p2.appLat * Math.PI / 180;
            double lon1 = p1.appLon * Math.PI / 180;
            double lon2 = p2.appLon * Math.PI / 180;
            if (lat1 == lat2 && lon1 == lon2)
            {
                return 0.0;
            }
            double d = Math.Acos(Math.Sin(lat1) * Math.Sin(lat2) + Math.Cos(lat1) * Math.Cos(lat2) * Math.Cos(lon2 - lon1)) * R;
            return d * 1000;
        }

        // 두 보정된 위치간의 거리 계산(걍)
        private double getDistance(double lat1, double lat2, double lon1, double lon2)
        {
            double R = 6371;
            double alat1 = lat1 * Math.PI / 180;
            double alat2 = lat2 * Math.PI / 180;
            double alon1 = lon1 * Math.PI / 180;
            double alon2 = lon2 * Math.PI / 180;
            if (lat1 == lat2 && lon1 == lon2)
            {
                return 0.0;
            }
            double d = Math.Acos(Math.Sin(alat1) * Math.Sin(alat2) + Math.Cos(alat1) * Math.Cos(alat2) * Math.Cos(alon2 - alon1)) * R;
            return d * 1000;
        }

        
        private double getFitness(int mod_index)
        {
            double ret = 0.0;
            int start = mod_index - 2;
            if (start < 0)
                start = 0;
            int i = 0;
            //for (i = start; i < end; i++)
            {
                Pot pt0 = mCom.mPoint.mPt[i];
                
            //    mCom.mPoint.mPt[i].pen_a= get
            }
            return ret;
        }


        //now set 0.5
        private void setcoef_a()
        {
            foreach (Pot pt in mCom.mPoint.mPt)
            {
                if (pt.Lat == 0 || pt.Lon == 0 || pt.Lat == -1 || pt.Lon == -1)
                    continue;
                pt.coef_a = 0.5;
                if (pt.thro)
                {
                    pt.coef_a = 0.0;
                }
                //pt.coef_a = (180 - pt.acc_angle) / 90;
            }
            return;
        }

        private void setcoef_b()
        {
            foreach (Pot pt in mCom.mPoint.mPt)
            {
                if (pt.Lat == 0 || pt.Lon == 0 || pt.Lat == -1 || pt.Lon == -1)
                    continue;
                pt.coef_b = 1000;
            }
            return;
        }

        private void setcoef_c()
        {
            foreach (Pot pt in mCom.mPoint.mPt)
            {
                if (pt.Lat == 0 || pt.Lon == 0 || pt.Lat == -1 || pt.Lon == -1)
                    continue;
                pt.coef_c = 200;
            }
            return;
        }

        
        // 모든 점의 위치, 속도, 가속도, 각도를 지정하고 coef들을 한 번만 여기서 지정한다.
        private double getFitness(bool isfirst)
        {
            double ret = 0.0;
            //part a~part d 는 모두 페널티로 0보다 작은 값p
           // double parta = 0.0;           // 원위치와의 거리
            double partb = 0.0;           // 속도(250m/s 이상 페널티요)
            double partc = 0.0;           // 가속도(value)
            double partd = 0.0;           // 방향전환


            // 급가속 파트
            // 속도 계산
            double preappLat = 0.0;
            double preappLon = 0.0;
            DateTime pretime = mCom.mPoint.mPt[0].Time;
            Pot prevpoint = null;
            if (isfirst)
            {
                setcoef_b();
                setcoef_c();
            }
            foreach (Pot pt in mCom.mPoint.mPt)
            {
                if (pt.Lat == 0 || pt.Lon == 0 || pt.Lat == -1 || pt.Lon == -1)
                    continue;
                if (preappLat == pt.appLat && preappLon == pt.appLon)
                    continue;
                if (pretime == pt.Time || 
                   (preappLat == 0.0 && preappLon == 0.0) )
                {
                    preappLat = pt.appLat;
                    preappLon = pt.appLon;
                    pretime = pt.Time;
                    prevpoint = pt;
                    continue;
                }
                //그 이후에는 속도계산하여 저장       
                TimeSpan dt = pt.Time.Subtract(pretime);
                pt.tm_span = dt;
                pt.dist_prev = getDistance(preappLat, pt.appLat, preappLon, pt.appLon);
                pt.velocity = pt.dist_prev / dt.TotalSeconds;
                pt.tm_slope = Math.Atan2(pt.appLat - preappLat, pt.appLon - preappLon);
                if (pt.tm_slope < 0)
                {
                    pt.tm_slope = pt.tm_slope + 2 * Math.PI;
                }
                if (pt.velocity > 3) // no more 5m/s?!
                {
                    partb = partb - (pt.velocity - 3);
                    pt.pen_b = pt.coef_b * (pt.velocity - 3);                   //part b penalty
                }
                else
                {
                    pt.pen_b = 0.0;
                }
                //이전꺼.
                preappLat = pt.appLat;
                preappLon = pt.appLon;
                pretime = pt.Time;
                prevpoint = pt;
            }
            //같은 방법으로 가속도계산
            double prevelo = 0.0;
            TimeSpan prespan = mCom.mPoint.mPt[0].tm_span;
            double preangle = 0.0;
            pretime = mCom.mPoint.mPt[0].Time;
            double accel = 0.0;
            double accel_angle = 0.0;
            foreach (Pot pt in mCom.mPoint.mPt)
            {
                if (pt.Lat == 0 || pt.Lon == 0 || pt.Lat == -1 || pt.Lon == -1)
                    continue;
                if (pt.velocity == 0.0)
                    continue;
                if (pretime == pt.Time)
                    continue;
                //첫 포인트면 저장만
                if (prevelo == 0.0)
                {
                    prevelo = pt.velocity;
                    prespan = pt.tm_span;
                    preangle = pt.tm_slope;
                    pretime = pt.Time;
                    prevpoint = pt;
                    continue;
                }
                //그 이후에는 가속도계산하여 누적 & 저장
                accel = Math.Abs(pt.velocity - prevelo) / ((prespan.Add(pt.tm_span)).TotalSeconds / 2);
                prevpoint.accel = accel;
                prevpoint.pen_c = prevpoint.coef_c * accel;                         // part c penalty
                partc = partc - accel;
                //급가속 계산(각도)
                double tempan;
                preangle = preangle + Math.PI;
                if (preangle > 2 * Math.PI)
                {
                    preangle = preangle - 2 * Math.PI;
                }
                if (pt.tm_slope > preangle)
                {
                    tempan = pt.tm_slope - preangle;
                }
                else
                {
                    tempan = preangle - pt.tm_slope;
                }
                if (tempan > Math.PI)
                {
                    tempan = Math.PI * 2 - tempan;
                }
                accel_angle = tempan * 180 / Math.PI;
                prevpoint.acc_angle = accel_angle;
                partd = partd - (180 - accel_angle);
                
                if (0 < accel_angle && 80.0 > accel_angle
                    || prevpoint.thro)
                { prevpoint.moveable = true; }
                else
                { prevpoint.moveable = false; }
                
                //이전꺼 저장
                prevelo = pt.velocity;
                prespan = pt.tm_span;
                preangle = pt.tm_slope;
                pretime = pt.Time;
                prevpoint = pt;
            }

            // 계수 조정(part a only)
            if (isfirst)
            {
                setcoef_a();
            }
            foreach (Pot pt in mCom.mPoint.mPt)
            {
                if (pt.Lat == 0 || pt.Lon == 0 || pt.Lat == -1 || pt.Lon == -1)
                    continue;
                pt.pen_a = pt.coef_a * getDistance(pt);
                ret = ret - pt.pen_a - pt.pen_b - pt.pen_c;
            }
            return ret;
        }


        private double getFitness_old()
        {
            double ret = 0.0;
            //part a~part d 는 모두 페널티로 0보다 작은 값p
            double parta = 0.0;           // 원위치와의 거리
            double partb = 0.0;           // 속도(250m/s 이상 페널티요)
            double partc = 0.0;           // 가속도(value)
            double partd = 0.0;           // 방향전환
            // 원위치와의 거리 파트

            foreach (Pot pt in mCom.mPoint.mPt)
            {
                if (pt.Lat == 0 || pt.Lon == 0 || pt.Lat == -1 || pt.Lon == -1)
                    continue;

                parta = parta - getDistance(pt);
            }
            // 급가속 파트
            // 속도 계산
            double preappLat = 0.0;
            double preappLon = 0.0;
            DateTime pretime = mCom.mPoint.mPt[0].Time;
            Pot prevpoint = null;
            foreach (Pot pt in mCom.mPoint.mPt)
            {
                if (pt.Lat == 0 || pt.Lon == 0 || pt.Lat == -1 || pt.Lon == -1)
                    continue;
                if (preappLat == pt.appLat && preappLon == pt.appLon)
                    continue;
                if (pretime == pt.Time)
                    continue;
                //첫 포인트면 저장만
                if (preappLat == 0.0 && preappLon == 0.0)
                {
                    preappLat = pt.appLat;
                    preappLon = pt.appLon;
                    pretime = pt.Time;
                    prevpoint = pt;
                    continue;
                }
                //그 이후에는 속도계산하여 저장       
                TimeSpan dt = pt.Time.Subtract(pretime);
                pt.tm_span = dt;
                pt.dist_prev = getDistance(preappLat, pt.appLat, preappLon, pt.appLon);
                pt.velocity = pt.dist_prev / dt.TotalSeconds;
                pt.tm_slope = Math.Atan2(pt.appLat - preappLat, pt.appLon - preappLon);
                if (pt.tm_slope < 0)
                {
                    pt.tm_slope = pt.tm_slope + 2 * Math.PI;
                }
                if (pt.velocity > 3) // no more 5m/s?!
                {
                    partb = partb - (pt.velocity - 3);
                }
                //이전꺼.
                preappLat = pt.appLat;
                preappLon = pt.appLon;
                pretime = pt.Time;
                prevpoint = pt;
            }
            //같은 방법으로 가속도계산
            double prevelo = 0.0;
            TimeSpan prespan = mCom.mPoint.mPt[0].tm_span;
            double preangle = 0.0;
            pretime = mCom.mPoint.mPt[0].Time;
            double accel = 0.0;
            double accel_angle = 0.0;
            foreach (Pot pt in mCom.mPoint.mPt)
            {
                if (pt.Lat == 0 || pt.Lon == 0 || pt.Lat == -1 || pt.Lon == -1)
                    continue;
                if (pt.velocity == 0.0)
                    continue;
                if (pretime == pt.Time)
                    continue;
                //첫 포인트면 저장만
                if (prevelo == 0.0)
                {
                    prevelo = pt.velocity;
                    prespan = pt.tm_span;
                    preangle = pt.tm_slope;
                    pretime = pt.Time;
                    prevpoint = pt;
                    continue;
                }
                //그 이후에는 가속도계산하여 누적 & 저장
                accel = Math.Abs(pt.velocity - prevelo) / ((prespan.Add(pt.tm_span)).TotalSeconds / 2);
                prevpoint.accel = accel;
                partc = partc - accel;
                //급가속 계산(각도)
                double tempan;
                preangle = preangle + Math.PI;
                if (preangle > 2 * Math.PI)
                {
                    preangle = preangle - 2 * Math.PI;
                }
                if (pt.tm_slope > preangle)
                {
                    tempan = pt.tm_slope - preangle;
                }
                else
                {
                    tempan = preangle - pt.tm_slope;
                }
                if (tempan > Math.PI)
                {
                    tempan = Math.PI * 2 - tempan;
                }
                accel_angle = tempan * 180 / Math.PI;
                prevpoint.acc_angle = accel_angle;
                partd = partd - (180 - accel_angle);
                
                if (0 < accel_angle && 80.0 > accel_angle)
                { prevpoint.moveable = true; }
                else
                { prevpoint.moveable = false; }
                
                //이전꺼 저장
                prevelo = pt.velocity;
                prespan = pt.tm_span;
                preangle = pt.tm_slope;
                pretime = pt.Time;
                prevpoint = pt;
            }

            //ret = partd;
            ret = 0.5 * parta + 1000.0 * partb + 200.0 * partc;// +100 * partd;
            //ret = 0.5 * parta + 100000.0 * partb;
            return ret;
        }

        private void approximate()
        {
            double prevfitness;
            double fitness;
            prevfitness = getFitness(false);
            Random ran = new Random();
            int index;
            double dlat = 0.0;
            double dlon = 0.0;
            for (int a = 0; a < mCom.mPoint.mPt.Count() * 2200; a++)
            {
                index = ran.Next(mCom.mPoint.mPt.Count());
                if (mCom.mPoint.mPt[index].moveable == false)
                    continue;
                if (mCom.mPoint.mPt[index].Lat == 0.0 || mCom.mPoint.mPt[index].Lon == 0.0 || mCom.mPoint.mPt[index].Lon == -1 || mCom.mPoint.mPt[index].Lat == -1)
                    continue;
                if (index >= 1 && mCom.mPoint.mPt[index].Lat == mCom.mPoint.mPt[index - 1].Lat && mCom.mPoint.mPt[index].Lon == mCom.mPoint.mPt[index - 1].Lon)
                    continue;
                dlat = (ran.NextDouble() - 0.5) * 0.004;
                dlon = (ran.NextDouble() - 0.5) * 0.004;
                dlat = Math.Round(dlat, 5);
                dlon = Math.Round(dlon, 5);
                mCom.mPoint.mPt[index].appLat += dlat;
                mCom.mPoint.mPt[index].appLon += dlon;
                fitness = getFitness(false);
                if (fitness <= prevfitness)
                {
                    mCom.mPoint.mPt[index].appLon -= dlon;
                    mCom.mPoint.mPt[index].appLat -= dlat;
                }
                else
                {
                    prevfitness = fitness;
                }
            }

        }

        private void UpdatePic(string input,NextViewer nv)
        {

            
            string cmd = input.Substring(2);
            string[] cmds = cmd.Split(' ');
            if (cmds[0] == "update")
            {
                for (int i = 1; i < cmds.Length-1; i+=2)
                {
                    if (cmds[i] == "-u")
                    {
                        mCom.user = cmds[i + 1];
                    }
                    else if (cmds[i] == "-d")
                    {
                    }
                    else if (cmds[i] == "-o")
                    {
                        try
                        {
                            nv.SetOpacity(double.Parse(cmds[i + 1]));
                        }
                        catch (Exception ee)
                        {
                            nv.SetText(ee.ToString());
                        }
                    }
                    else if (cmds[i] == "-ps")
                    {
                        try
                        {
                            mCom.dtStart = DateTime.Parse(cmds[i + 1] + " " + cmds[i + 2]);
                        }
                        catch (Exception)
                        {
                            nv.SetText("입력은 : yyyy-mm-dd HH:MM 형태입니다.");
                            return;
                        }
                        i++;
                    }
                    else if (cmds[i] == "-pe")
                    {
                        try
                        {
                            mCom.dtEnd = DateTime.Parse(cmds[i + 1] + " " + cmds[i + 2]);
                        }
                        catch (Exception)
                        {
                            nv.SetText("입력은 : yyyy-mm-dd HH:MM 형태입니다.");
                            return;
                        }
                        i++;
                    }
                    else if (cmds[i] == "-p")
                    {

                    }
                }

                nv.SetText(mCom.map.ToUri().ToString());
            }
            else if(cmds[0] == "run")
            {
                mCom.GetRawData();
                mCom.UpdatePointsForLocation();
                mCom.PrintVelocity();
                mCom.MakeHttpRequest();
                nv.updatePic(mCom.map);
            }
            else if (cmds[0] == "comp")
            {

            }
            //nv.SetText("update -u(mCom.user) -d(date) -p(path) -ps(period start) -pe(period end) -c(center) -z(zoom) -f(format) -mt(mCom.maptype) -mo(mobile) -s(sensor)");


        }
        
        
        


        private void MakeHttpRequest_app()
        {
            
            DateTime prev = new DateTime();
            double preLat = 0;
            double preLon = 0;

            mCom.map.InitPath();

            foreach (Pot pt in mCom.mPoint.mPt)
            {
                if (pt.Lat == 0 || pt.Lon == 0 || pt.Lat == -1 || pt.Lon == -1)
                    continue;
                if (preLat == pt.appLat && preLon == pt.appLon)
                    continue;
                if (pt.GPS == 1 && prev.Hour == pt.Time.Hour && prev.Minute == pt.Time.Minute)
                {
                    continue;
                }
                preLat = pt.appLat;
                preLon = pt.appLon;

                if(mCom.map.AddPath(preLat.ToString("0.0000"), preLon.ToString("0.0000"))
                    == false)
                    break;
                prev = pt.Time;

            }
            

        }

        
       
        private string addon="";
        //private StaticMaps.map mCom.map;
        //private string user;
        //private DateTime dtStart;
        //private DateTime dtEnd;
        //private Points mCom.mPoint;
        public List<string> mCmds;
        public Commands mCom;
        
    }
}
